﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using ABCat.Core.Plugins.DataProviders;
using ABCat.Core.Plugins.DataSets;
using ABCat.Shared.Properties;

namespace ABCat.Core.Plugins.Catalog.GrouppingLogics
{
	/// <summary>
	/// Базовый класс логики группировки записей
	/// </summary>
	public abstract class GrouppingLogicPluginBase : IGrouppingLogicPlugin
	{
		#region + Events +

		/// <summary>
		/// Логика группировки уничтожена
		/// </summary>
		public event EventHandler Disposed;
		/// <summary>
		/// Значение свойства изменено
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
		#region + Fields +

		private bool _isOnUpdate;

		#endregion
		#region + Properties +

		public Config Config		{get; set;}
		/// <summary>
		/// Контекст приложения
		/// </summary>
		public Context Context		{get; protected set;}
		/// <summary>
		/// В данный момент выполняется асинхронная операция
		/// </summary>
		public bool IsOnUpdate
		{
			get { return _isOnUpdate; }
			set
			{
				if (value.Equals(_isOnUpdate)) return;
				_isOnUpdate = value;
				OnPropertyChanged();
			}
		}

		/// <summary>
		/// Название логики группировки
		/// </summary>
		public abstract string Name		{get;}

		#endregion
		#region + Ctor +

		/// <summary>
		/// Базовый класс логики группировки
		/// </summary>
		/// <param name="context">Контекст приложения</param>
		protected GrouppingLogicPluginBase(Context context)
		{
			Context = context;
		}

		#endregion
		#region + Logic +

		/// <summary>
		/// Начать асинхронную генерацию дерева групп
		/// </summary>
		/// <param name="completedCallback">Метод завершения операции</param>
		/// <param name="cancellationToken">Токен отмены операции</param>
		public void BeginGenerateGroupsAsync(Action<Group, Exception> completedCallback, CancellationToken cancellationToken)
		{
			IsOnUpdate = true;
			Task.Factory.StartNew(
				() =>
				{
					try
					{
						var result = GenerateGroupsInternal(cancellationToken);
						completedCallback(result, null);
					}
					catch (Exception ex)
					{
						completedCallback(null, ex);
					}
					finally
					{
						IsOnUpdate = false;
					}
				}, cancellationToken);
		}

		/// <summary>
		/// Начать асинхронное получение записей, включенных в группу
		/// </summary>
		/// <param name="dbContainer">Контейнер БД</param>
		/// <param name="group">Группа записей</param>
		/// <param name="completedCallback">Метод завершения операции</param>
		/// <param name="cancellationToken">Токен отмены операции</param>
		public void BeginGetRecordsAsync(IDbContainer dbContainer, Group group, Action<IEnumerable<IAudioBook>, Exception> completedCallback, CancellationToken cancellationToken)
		{
			Task.Factory.StartNew(
				() =>
				{
					try
					{
						IsOnUpdate = true;
						completedCallback(GetRecordsInner(dbContainer, group, cancellationToken), null);
					}
					catch (Exception ex)
					{
						completedCallback(null, ex);
					}
					finally
					{
						IsOnUpdate = false;
					}
				}, cancellationToken);
		}

		public abstract bool CheckForConfig(bool correct, out Config incorrectConfig);

		/// <summary>
		/// Уничтожение экземпляра класса логики группировки
		/// </summary>
		public void Dispose()
		{
			Disposed.Fire(this);
		}

		public override string ToString()
		{
			return Name;
		}

		/// <summary>
		/// Логика формирования дерева групп
		/// </summary>
		/// <param name="cancellationToken">Токен отмены операции</param>
		/// <returns>Корневая группа сформированного дерева</returns>
		protected abstract Group GenerateGroupsInternal(CancellationToken cancellationToken);

		/// <summary>
		/// Логика получения списка записей, включенных в группу
		/// </summary>
		/// <param name="dbContainer">Контейнер БД</param>
		/// <param name="group">Группа записей</param>
		/// <param name="cancellationToken">Токен отмены операции</param>
		/// <returns>Список записей, включенных в группу</returns>
		protected abstract IEnumerable<IAudioBook> GetRecordsInner(IDbContainer dbContainer, Group group, CancellationToken cancellationToken);

		[NotifyPropertyChangedInvocator]
		protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
		}

		#endregion
	}
}